/************************************************************************************
**  
* @copyright (c) 2015-, Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file     duye_cap_http_parser.cpp
* @version     
* @brief      
* @author   
* @date     2013-04-13
* @note 
*
*  1. 2018-04-13 created this file
* 
*/
#include <ctype.h>
#include <duye_logger.h>
#include <duye_cap_http_parser.h>

static const int8* DUYE_LOG_PREFIX = "duye.cap.http.parser";

#ifndef ULLONG_MAX
#define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
#endif

namespace duye {

/* http methods strings, correspond with enum HttpMethod */
static const std::string s_methodString[] = {
	"DELETE",
	"GET",
	"HEAD",
	"POST",
	"PUT",
	/* pathological */
	"CONNECT",
	"OPTIONS",
	"TRACE",
	/* webdav */
	"COPY",
	"LOCK",
	"MKCOL",
	"MOVE",
	"PROPFIND",
	"PROPPATCH",
	"SEARCH",
	"UNLOCK",
	/* subversion */ 
	"REPORT",
	"MKACTIVITY",
	"CHECKOUT",
	"MERGE",
	/* upnp */ 
	"M-SEARCH",
	"NOTIFY",
	"SUBSCRIBE",
	"UNSUBSCRIBE",
	/* RFC-5789 */
	"PATCH",
	"PURGE",
	/* CalDAV */
	"MKCALENDAR",
	/* Unknown */
	"<UNKNOWN>"
};

static const int8 CR = '\r';
static const int8 LF = '\n';

/* http parser error strings table, correspond with enum HttpParserErrno */
static const struct {
	const int8* name;
	const int8* description;
} s_httpParserStrErrorTable[] =
{
	/* No error */
	{"HPE_OK", "success"},
	/* Callback-related errors */
	{"HPE_CB_MESSAGE_BEGIN", "the on_message_begin callback failed"},
	{"HPE_CB_URL", "the on_url callback failed"},
	{"HPE_CB_HEADER_FIELD", "the on_header_field callback failed"},
	{"HPE_CB_HEADER_VALUE", "the on_header_value callback failed"},
	{"HPE_CB_HEADERS_COMPLETE", "the on_headers_complete callback failed"},
	{"HPE_CB_BODY", "the on_body callback failed"},
	{"HPE_CB_MESSAGE_COMPLETE", "the on_message_complete callback failed"},
	{"HPE_CB_STATUS", "the on_status callback failed"},
	/* Parsing-related errors */
	{"HPE_INVALID_EOF_STATE", "stream ended at an unexpected time"},
	{"HPE_HEADER_OVERFLOW", "too many header bytes seen; overflow detected"},
	{"HPE_CLOSED_CONNECTION", "data received after completed connection: close message"},
	{"HPE_INVALID_VERSION", "invalid HTTP version"},
	{"HPE_INVALID_STATUS", "invalid HTTP status code"},
	{"HPE_INVALID_METHOD", "invalid HTTP method"},
	{"HPE_INVALID_URL", "invalid URL"},
	{"HPE_INVALID_HOST", "invalid host"},
	{"HPE_INVALID_PORT", "invalid port"},
	{"HPE_INVALID_PATH", "invalid path"},
	{"HPE_INVALID_QUERY_STRING", "invalid query string"},
	{"HPE_INVALID_FRAGMENT", "invalid fragment"},
	{"HPE_LF_EXPECTED", "LF character expected"},
	{"HPE_INVALID_HEADER_TOKEN", "invalid character in header"},
	{"HPE_INVALID_CONTENT_LENGTH", "invalid character in content-length header"},
	{"HPE_INVALID_CHUNK_SIZE", "invalid character in chunk size header"},
	{"HPE_INVALID_CONSTANT", "invalid constant string"},
	{"HPE_INVALID_INTERNAL_STATE", "encountered unexpected internal state"},
	{"HPE_STRICT", "strict mode assertion failed"},
	{"HPE_PAUSED", "parser is paused"},
	{"HPE_UNKNOWN", "an unknown error occurred"}
}; 

/* Tokens as defined by rfc 2616. Also lowercases them.
 *        token       = 1*<any CHAR except CTLs or separators>
 *     separators     = "(" | ")" | "<" | ">" | "@"
 *                    | "," | ";" | ":" | "\" | <">
 *                    | "/" | "[" | "]" | "?" | "="
 *                    | "{" | "}" | SP | HT
 */
static const char s_tokens[256] = {
/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
        0,       0,      '*',     '+',      0,      '-',     '.',      0,
/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
       '8',     '9',      0,       0,       0,       0,       0,       0,
/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
       'x',     'y',     'z',      0,      '|',      0,      '~',       0 
};

static const int8 s_unhex[256] = {
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
	-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

#if HTTP_PARSER_STRICT
# define T(v) 0
#else
# define T(v) v
#endif

static const uint8 s_normalUrlChar[32] = {
	/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
	0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
	/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
	0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
	/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
	0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
	/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
	0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
	/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
	0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
	/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
	/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
	/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
	1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, 
};
#undef T

static const char s_PROXY_CONNECTION[] = "proxy-connection";
static const char s_CONNECTION[] = "connection";
static const char s_CONTENT_LENGTH[] = "content-length";
static const char s_TRANSFER_ENCODING[] = "transfer-encoding";
static const char s_UPGRADE[] = "upgrade";
static const char s_CHUNKED[] =  "chunked";
static const char s_KEEP_ALIVE[] = "keep-alive";
static const char s_CLOSE[] = "close";

HttpParserData::HttpParserData() {}
HttpParserData::~HttpParserData() {}

HttpParserType HttpParserData::getParserType() {
	return static_cast<HttpParserType>(m_type);
}

HttpParserType HttpParserData::getParserType() const {
	return static_cast<HttpParserType>(m_type);
}

uint16 HttpParserData::getHttpMajor() {
	return m_httpMajor;
}

uint16 HttpParserData::getHttpMajor() const {
	return m_httpMajor;
}

uint16 HttpParserData::getHttpMinor() {
	return m_httpMinor;
}

uint16 HttpParserData::getHttpMinor() const {
	return m_httpMinor;
}

uint32 HttpParserData::getStatusCode() {
	return m_statusCode;
}

uint32 HttpParserData::getStatusCode() const {
	return m_statusCode;
}

HttpMethod HttpParserData::getHttpMethod() {
	return static_cast<HttpMethod>(m_method);
}

HttpMethod HttpParserData::getHttpMethod() const {
	return static_cast<HttpMethod>(m_method);
}

HttpParserErrno HttpParserData::getParserErrno() {
	return static_cast<HttpParserErrno>(m_httpErrno);
}

HttpParserErrno HttpParserData::getParserErrno() const {
	return static_cast<HttpParserErrno>(m_httpErrno);
}

void* HttpParserData::getUserData() {
	return m_data;
}

void* HttpParserData::getUserData() const {
	return m_data;
}

HttpParser::HttpParser() {}
HttpParser::~HttpParser() {}

int32 HttpParser::parse(const uint8* data, int32 len) {
	uint8 c, ch;
	int8 unhexVal;
	const uint8* p = data;
	const uint8* headerFieldMark = NULL;
	const uint8* headerValueMark = NULL;
	const uint8* urlMark = NULL;
	const uint8* bodyMark = NULL;
	const uint8* statusMark = NULL;
	ParserState currentSate = static_cast<ParserState>(m_data.m_state);

	// TODO: init m_data ...???

	/* We're in an error state. Don't bother doing anything. */
	if(m_data.m_httpErrno != HPE_OK) {
		DUYE_DEBUG(" Http errno(%d, %s): %s", 
			m_data.m_httpErrno, 
			HttpParser::getErrnoName(static_cast<HttpParserErrno>(m_data.m_httpErrno)).c_str(),
			HttpParser::getErrnoDescription(static_cast<HttpParserErrno>(m_data.m_httpErrno)).c_str());
		return 0;
	}

	if (len == 0) {
		switch (currentSate) {
		case S_BODY_IDENTITY_EOF: {
				/* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
				* we got paused.
				*/
				if (m_data.m_httpErrno != HPE_OK) {
					goto ERR;
				}

				HttpParserListenerLst::iterator iter = m_listenerLst.begin();
				for (; iter != m_listenerLst.end(); ++iter)
				{
					if (*iter == NULL) {
						continue;
					}

					if ((*iter)->onMessageComplete(&m_data) != 0)
						m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

					if (m_data.m_httpErrno != HPE_OK)
						return (p - data);
				}
			}				
			return 0;
		case S_DEAD:
		case S_START_REQ_OR_RES:
		case S_START_RES:
		case S_START_REQ:
			return 0;
		default:
			m_data.m_httpErrno = HPE_INVALID_EOF_STATE;
			return 1;
		}
	}

	if (currentSate == S_HEADER_FIELD) {
		headerFieldMark = data;
	}

	if (currentSate == S_HEADER_VALUE) {
		headerValueMark = data;
	}

	switch (currentSate) {
	case S_REQ_PATH:
	case S_REQ_SCHEMA:
	case S_REQ_SCHEMA_SLASH:
	case S_REQ_SCHEMA_SLASH_SLASH:
	case S_REQ_SERVER_START:
	case S_REQ_SERVER:
	case S_REQ_SERVER_WITH_AT:
	case S_REQ_QUERY_STRING_START:
	case S_REQ_QUERY_STRING:
	case S_REQ_FRAGMENT_START:
	case S_REQ_FRAGMENT:
		urlMark = data;
		break;
	case S_RES_STATUS:
		statusMark = data;
		break;
	default:
		break;
	}

	for (p = data; p != data + len; p ++) {
		ch = *p;

		if (currentSate <= S_HEADERS_DONE) {
			m_data.m_nread += 1;
			if (m_data.m_nread > HTTP_MAX_HEADER_SIZE) {
				m_data.m_httpErrno = HPE_HEADER_OVERFLOW;
				goto ERR;
			}
		}

REEXECUTE:
		switch (currentSate) {
		case S_DEAD:
			/* this state is used after a 'Connection: close' message
			* the parser will error out if it reads another message
			*/
			if (ch == CR || ch == LF) {
				break;
			}

			m_data.m_httpErrno = HPE_CLOSED_CONNECTION;
			goto ERR;
		case S_START_REQ_OR_RES: {
				if (ch == CR || ch == LF)
					break;
				m_data.m_flags = 0;
				m_data.m_contentLength = ULLONG_MAX;

				if (ch == 'H') {
					currentSate = S_RES_OR_RESP_H;

					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					HttpParserListenerLst::iterator iter = m_listenerLst.begin();
					for (; iter != m_listenerLst.end(); iter++) {
						if (*iter == NULL) {
							DUYE_ERROR("*iter == NULL");
							goto ERR; 
						}

						if ((*iter)->onMessageBegin(&m_data) != 0) {
							m_data.m_httpErrno = HPE_L_MESSAGE_BEGIN;
						}

						if (m_data.m_httpErrno != HPE_OK) {
							return (p - data + 1);
						}
					}
				} else {
					m_data.m_type = HPT_REQUEST;
					currentSate = S_START_REQ;
					goto REEXECUTE;
				}
			}
			break;

		case S_RES_OR_RESP_H:
			if (ch == 'T') {
				m_data.m_type = HPT_RESPONSE;
				currentSate = S_RES_HT;
			} else {
				if (ch != 'E') {
					m_data.m_httpErrno = HPE_INVALID_CONSTANT;
					goto ERR;
				}

				m_data.m_type = HPT_REQUEST;
				m_data.m_method = HTTP_HEAD;
				m_data.m_index = 2;
				currentSate = S_REQ_METHOD;
			}
			break;

		case S_START_RES: {
				m_data.m_flags = 0;
				m_data.m_contentLength = ULLONG_MAX;

				switch (ch) {
				case 'H':
					currentSate = S_RES_H;
					break;

				case CR:
				case LF:
					break;

				default:
					m_data.m_httpErrno = HPE_INVALID_CONSTANT;
					goto ERR;
				}

				if (m_data.m_httpErrno != HPE_OK) goto ERR;
				HttpParserListenerLst::iterator iter = m_listenerLst.begin();
				for (; iter != m_listenerLst.end(); iter++) {
					if (*iter == NULL) {
						DUYE_ERROR("*iter == NULL");
						goto ERR; 
					}

					if ((*iter)->onMessageBegin(&m_data) != 0) {
						m_data.m_httpErrno = HPE_L_MESSAGE_BEGIN;
					}

					if (m_data.m_httpErrno != HPE_OK) {
						return (p - data + 1);
					}
				}
			}
			break;

		case S_RES_H:
			if (HttpParser::strictCheck(ch != 'T')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}

			currentSate = S_RES_HT;
			break;

		case S_RES_HT:
			if (HttpParser::strictCheck(ch != 'T')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_RES_HTT;
			break;
		case S_RES_HTT:
			if (HttpParser::strictCheck(ch != 'P')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_RES_HTTP;
			break;
		case S_RES_HTTP:
			if (HttpParser::strictCheck(ch != '/')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_RES_FIRST_HTTP_MAJOR;
			break;
		case S_RES_FIRST_HTTP_MAJOR:
			if (ch < '1' || ch > '9') {
				m_data.m_httpErrno = HPE_INVALID_VERSION;
				goto ERR;
			}

			m_data.m_httpMajor = ch - '0';
			currentSate = S_RES_HTTP_MAJOR;
			break;

		/* major HTTP version or dot */
		case S_RES_HTTP_MAJOR: {
				if (ch == '.') {
					currentSate = S_RES_FIRST_HTTP_MINOR;
					break;
				}

				if (!isdigit(ch)) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}

				m_data.m_httpMajor *= 10;
				m_data.m_httpMajor += ch - '0';

				if (m_data.m_httpMajor > 999) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}
			}
			break;

		/* first digit of minor HTTP version */
		case S_RES_FIRST_HTTP_MINOR:
			if (!isdigit(ch)) {
				m_data.m_httpErrno = HPE_INVALID_VERSION;
				goto ERR;
			}

			m_data.m_httpMinor = ch - '0';
			currentSate = S_RES_HTTP_MINOR;
			break;

		/* minor HTTP version or end of request line */
		case S_RES_HTTP_MINOR: {
				if (ch == ' ') {
					currentSate = S_RES_FIRST_STATUS_CODE;
					break;
				}

				if (!isdigit(ch)) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}

				m_data.m_httpMinor *= 10;
				m_data.m_httpMinor += ch - '0';

				if (m_data.m_httpMinor > 999) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}
			}
			break;
		case S_RES_FIRST_STATUS_CODE: {
				if (!isdigit(ch)) {
					if (ch == ' ') {
						break;
					}

					m_data.m_httpErrno = HPE_INVALID_STATUS;
					goto ERR;
				}
				m_data.m_statusCode = ch - '0';
				currentSate = S_RES_STATUS_CODE;
			}
			break;

		case S_RES_STATUS_CODE: {
				if (!isdigit(ch)) {
					switch (ch) {
					case ' ':
						currentSate = S_RES_STATUS_START;
						break;
					case CR:
						currentSate = S_RES_LINE_ALMOST_DONE;
						break;
					case LF:
						currentSate = S_HEADER_FIELD_START;
						break;
					default:
						m_data.m_httpErrno = HPE_INVALID_STATUS;
						goto ERR;
					}
					break;
				}

				m_data.m_statusCode *= 10;
				m_data.m_statusCode += ch - '0';

				if (m_data.m_statusCode > 999) {
					m_data.m_httpErrno = HPE_INVALID_STATUS;
					goto ERR;
				}
			}
			break;
		case S_RES_STATUS_START: {
				if (ch == CR) {
					currentSate = S_RES_LINE_ALMOST_DONE;
					break;
				}

				if (ch == LF) {
					currentSate = S_HEADER_FIELD_START;
					break;
				}

				if (!statusMark) {
					statusMark = p;
				}
				currentSate = S_RES_STATUS;
				m_data.m_index = 0;				
			}
			break;

		case S_RES_STATUS:
			if (ch == CR) {
				currentSate = S_RES_LINE_ALMOST_DONE;

				if (m_data.m_httpErrno != HPE_OK) {
					DUYE_ERROR("m_data.m_httpErrno != HPE_OK");
					goto ERR;
				}

				if (statusMark) {
					HttpParserListenerLst::iterator iter = m_listenerLst.begin();
					for (; iter != m_listenerLst.end(); iter++) {
						if (*iter != NULL) {
							DUYE_ERROR("*iter != NULL");
							goto ERR;
						}						

						if ((*iter)->onStatus(&m_data, statusMark, p - statusMark) != 0) {
							m_data.m_httpErrno = HPE_L_STATUS;
						}

						if (m_data.m_httpErrno != HPE_OK) {
							return (p - data + 1);
						}
					}
					statusMark = NULL;
				}

				break;
			}

			if (ch == LF) {
				currentSate = S_HEADER_FIELD_START;

				if (m_data.m_httpErrno != HPE_OK) {
					DUYE_ERROR("m_data.m_httpErrno != HPE_OK");
					goto ERR;
				}

				if (statusMark) {
					HttpParserListenerLst::iterator iter = m_listenerLst.begin();
					for (; iter != m_listenerLst.end(); iter++) {
						if (*iter != NULL) {
							DUYE_ERROR("*iter != NULL");
							goto ERR;
						}

						if ((*iter)->onStatus(&m_data, statusMark, p - statusMark) != 0) {
							m_data.m_httpErrno = HPE_L_STATUS;
						}

						if (m_data.m_httpErrno != HPE_OK) {
							return (p - data + 1);
						}
					}
					statusMark = NULL;
				}

				break;
			}

			break;

		case S_RES_LINE_ALMOST_DONE:
			if (HttpParser::strictCheck(ch != LF)) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}

			currentSate = S_HEADER_FIELD_START;
			break;

		case S_START_REQ: {
				if (ch == CR || ch == LF) {
					break;
				}

				m_data.m_flags = 0;
				m_data.m_contentLength = ULLONG_MAX;

				if (!isalpha(ch)) {
					m_data.m_httpErrno = HPE_INVALID_METHOD;
					goto ERR;
				}

				m_data.m_method = HTTP_UNKNOWN;
				m_data.m_index = 1;
				switch (ch) {
				case 'C': 
					m_data.m_method = HTTP_CONNECT; /* or COPY, CHECKOUT */ 
					break;
				case 'D': 
					m_data.m_method = HTTP_DELETE; 
					break;
				case 'G': 
					m_data.m_method = HTTP_GET; 
					break;
				case 'H': 
					m_data.m_method = HTTP_HEAD; 
					break;
				case 'L': 
					m_data.m_method = HTTP_LOCK; 
					break;
				case 'M': 
					m_data.m_method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ 
					break;
				case 'N': 
					m_data.m_method = HTTP_NOTIFY; 
					break;
				case 'O': 
					m_data.m_method = HTTP_OPTIONS;
					break;
				case 'P': 
					m_data.m_method = HTTP_POST; /* or PROPFIND, PROPPATCH, PUT, PATCH, PURGE */					
					break;
				case 'R': 
					m_data.m_method = HTTP_REPORT; 
					break;
				case 'S': 
					m_data.m_method = HTTP_SUBSCRIBE; /* or SEARCH */ 
					break;
				case 'T': 
					m_data.m_method = HTTP_TRACE; 
					break;
				case 'U': 
					m_data.m_method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ 
					break;
				default:
					m_data.m_httpErrno = HPE_INVALID_METHOD;
					goto ERR;
				}
				currentSate = S_REQ_METHOD;
				
				if (m_data.m_httpErrno != HPE_OK) goto ERR;
				HttpParserListenerLst::iterator iter = m_listenerLst.begin();
				for (; iter != m_listenerLst.end(); iter++) {
					if (*iter != NULL) {
						DUYE_ERROR("*iter != NULL");
						goto ERR;
					}

					if ((*iter)->onMessageBegin(&m_data) != 0) {
						m_data.m_httpErrno = HPE_L_MESSAGE_BEGIN;
					}

					if (m_data.m_httpErrno != HPE_OK) {
						return (p - data + 1);
					}
				}
			}
			break;

		case S_REQ_METHOD: {
				const char *matcher;
				if (ch == '\0') {
					m_data.m_httpErrno = HPE_INVALID_METHOD;
					goto ERR;
				}

				matcher = s_methodString[m_data.m_method].c_str();
				if (ch == ' ' && matcher[m_data.m_index] == '\0') {
					currentSate = S_REQ_SPACES_BEFORE_URL;
				} else if (ch == matcher[m_data.m_index]) {
					/* nada */
				} else if (m_data.m_method == HTTP_CONNECT) {
					if (m_data.m_index == 1 && ch == 'H') {
						m_data.m_method = HTTP_CHECKOUT;
					} else if (m_data.m_index == 2  && ch == 'P') {
						m_data.m_method = HTTP_COPY;
					} else {
						m_data.m_httpErrno = HPE_INVALID_METHOD;
						goto ERR;
					}
				} else if (m_data.m_method == HTTP_MKCOL) {
					if (m_data.m_index == 1 && ch == 'O') {
						m_data.m_method = HTTP_MOVE;
					} else if (m_data.m_index == 1 && ch == 'E') {
						m_data.m_method = HTTP_MERGE;
					} else if (m_data.m_index == 1 && ch == '-') {
						m_data.m_method = HTTP_MSEARCH;
					} else if (m_data.m_index == 2 && ch == 'A') {
						m_data.m_method = HTTP_MKACTIVITY;
					} else if (m_data.m_index == 3 && ch == 'A') {
						m_data.m_method = HTTP_MKCALENDAR;
					} else {
						m_data.m_httpErrno = HPE_INVALID_METHOD;
						goto ERR;
					}
				} else if (m_data.m_method == HTTP_SUBSCRIBE) {
					if (m_data.m_index == 1 && ch == 'E') {
						m_data.m_method = HTTP_SEARCH;
					} else {
						m_data.m_httpErrno = HPE_INVALID_METHOD;
						goto ERR;
					}
				} else if (m_data.m_index == 1 && m_data.m_method == HTTP_POST) {
					if (ch == 'R') {
						m_data.m_method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
					} else if (ch == 'U') {
						m_data.m_method = HTTP_PUT; /* or HTTP_PURGE */
					} else if (ch == 'A') {
						m_data.m_method = HTTP_PATCH;
					} else {
						m_data.m_httpErrno = HPE_INVALID_METHOD;
						goto ERR;
					}
				} else if (m_data.m_index == 2) {
					if (m_data.m_method == HTTP_PUT) {
						if (ch == 'R') {
							m_data.m_method = HTTP_PURGE;
						} else {
							m_data.m_httpErrno = HPE_INVALID_METHOD;
							goto ERR;
						}
					} else if (m_data.m_method == HTTP_UNLOCK) {
						if (ch == 'S') {
							m_data.m_method = HTTP_UNSUBSCRIBE;
						} else {
							m_data.m_httpErrno = HPE_INVALID_METHOD;
							goto ERR;
						}
					} else {
						m_data.m_httpErrno = HPE_INVALID_METHOD;
						goto ERR;
					}
				} else if (m_data.m_index == 4 && m_data.m_method == HTTP_PROPFIND && ch == 'P') {
					m_data.m_method = HTTP_PROPPATCH;
				} else {
					m_data.m_httpErrno = HPE_INVALID_METHOD;
					goto ERR;
				}

				++ m_data.m_index;
			}
			break;

		case S_REQ_SPACES_BEFORE_URL: {
				if (ch == ' ') {
					break;
				}

				if (!urlMark) {
					urlMark = p;
				}

				if (m_data.m_method == HTTP_CONNECT) {
					currentSate = S_REQ_SERVER_START;
				}

				currentSate = parseUrlChar(static_cast<ParserState>(currentSate), ch);
				if (currentSate == S_DEAD) {
					m_data.m_httpErrno = HPE_INVALID_URL;
					goto ERR;
				}
			}
			break;

		case S_REQ_SCHEMA:
		case S_REQ_SCHEMA_SLASH:
		case S_REQ_SCHEMA_SLASH_SLASH:
		case S_REQ_SERVER_START: {
				switch (ch) {
				/* No whitespace allowed here */
				case ' ':
				case CR:
				case LF:
					m_data.m_httpErrno = HPE_INVALID_URL;
					goto ERR;
				default:
					currentSate = parseUrlChar(static_cast<ParserState>(currentSate), ch);
					if (currentSate == S_DEAD) {
						m_data.m_httpErrno = HPE_INVALID_URL;
						goto ERR;
					}
				}
			}
			break;

		case S_REQ_SERVER:
		case S_REQ_SERVER_WITH_AT:
		case S_REQ_PATH:
		case S_REQ_QUERY_STRING_START:
		case S_REQ_QUERY_STRING:
		case S_REQ_FRAGMENT_START:
		case S_REQ_FRAGMENT: {
				switch (ch) {
				case ' ':
					currentSate = S_REQ_HTTP_START;

					if (m_data.m_httpErrno != HPE_OK) {
						DUYE_ERROR("m_data.m_httpErrno == HPE_OK");
						goto ERR;
					}

					if (urlMark) {
						HttpParserListenerLst::iterator iter = m_listenerLst.begin();
						for (; iter != m_listenerLst.end(); iter++) {
							if (*iter == NULL) {
								DUYE_ERROR("*iter == NULL");
								goto ERR;
							}

							if ((*iter)->onUrl(&m_data, urlMark, p - urlMark) != 0) {
								m_data.m_httpErrno = HPE_L_URL;
							}

							if (m_data.m_httpErrno != HPE_OK) {
								return (p - data + 1);
							}
						}
						urlMark = NULL;
					}

					break;
				case CR:
				case LF:
					m_data.m_httpMajor = 0;
					m_data.m_httpMinor = 9;
					currentSate = ((ch == CR) ? S_REQ_LINE_ALMOST_DONE : S_HEADER_FIELD_START);

					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					if (urlMark) {
						HttpParserListenerLst::iterator iter = m_listenerLst.begin();
						for (; iter != m_listenerLst.end(); iter++) {
							if (*iter == NULL) {
								DUYE_ERROR("*iter == NULL");
								goto ERR;
							}

							if ((*iter)->onUrl(&m_data, urlMark, p - urlMark) != 0) {
								m_data.m_httpErrno = HPE_L_URL;
							}

							if (m_data.m_httpErrno != HPE_OK) {
								return (p - data + 1);
							}
						}
						urlMark = NULL;
					}

					break;
				default:
					currentSate = parseUrlChar(static_cast<ParserState>(currentSate), ch);
					if (currentSate == S_DEAD) {
						m_data.m_httpErrno = HPE_INVALID_URL;
						goto ERR;
					}
				}
			}
			break;

		case S_REQ_HTTP_START:
			switch (ch) {
			case 'H':
				currentSate = S_REQ_HTTP_H;
				break;
			case ' ':
				break;
			default:
				m_data.m_httpErrno = HPE_INVALID_CONSTANT;
				goto ERR;
			}
			break;

		case S_REQ_HTTP_H:
			if(HttpParser::strictCheck(ch != 'T')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_REQ_HTTP_HT;
			break;

		case S_REQ_HTTP_HT:
			if(HttpParser::strictCheck(ch != 'T')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_REQ_HTTP_HTT;
			break;

		case S_REQ_HTTP_HTT:
			if(HttpParser::strictCheck(ch != 'P')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_REQ_HTTP_HTTP;
			break;

		case S_REQ_HTTP_HTTP:
			if(HttpParser::strictCheck(ch != '/')) {
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_REQ_FIRST_HTTP_MAJOR;
			break;

		/* first digit of major HTTP version */
		case S_REQ_FIRST_HTTP_MAJOR:
			if (ch < '1' || ch > '9') {
				m_data.m_httpErrno = HPE_INVALID_VERSION;
				goto ERR;
			}

			m_data.m_httpMajor = ch - '0';
			currentSate = S_REQ_HTTP_MAJOR;
			break;

		/* major HTTP version or dot */
		case S_REQ_HTTP_MAJOR: {
				if (ch == '.') {
					currentSate = S_REQ_FIRST_HTTP_MINOR;
					break;
				}

				if (!isdigit(ch)) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}

				m_data.m_httpMajor *= 10;
				m_data.m_httpMajor += ch - '0';

				if (m_data.m_httpMajor > 999) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}
			}
			break;

		/* first digit of minor HTTP version */
		case S_REQ_FIRST_HTTP_MINOR:
			if (!isdigit(ch)) {
				m_data.m_httpErrno = HPE_INVALID_VERSION;
				goto ERR;
			}

			m_data.m_httpMinor = ch - '0';
			currentSate = S_REQ_HTTP_MINOR;
			break;

		/* minor HTTP version or end of request line */
		case S_REQ_HTTP_MINOR: {
				if (ch == CR) {
					currentSate = S_REQ_LINE_ALMOST_DONE;
					break;
				}

				if (ch == LF) {
					currentSate = S_HEADER_FIELD_START;
					break;
				}

				/* XXX allow spaces after digit? */
				if (isdigit(ch)) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}

				m_data.m_httpMinor *= 10;
				m_data.m_httpMinor += ch - '0';

				if (m_data.m_httpMinor > 999) {
					m_data.m_httpErrno = HPE_INVALID_VERSION;
					goto ERR;
				}
			}
			break;

		/* end of request line */
		case S_REQ_LINE_ALMOST_DONE: {
				if (ch != LF) {
					m_data.m_httpErrno = HPE_LF_EXPECTED;
					goto ERR;
				}

				currentSate = S_HEADER_FIELD_START;		
			}
			break;

		case S_HEADER_FIELD_START: {
				if (ch == CR) {
					currentSate = S_HEADERS_ALMOST_DONE;
					break;
				}

				if (ch == LF) {
					/* they might be just sending \n instead of \r\n so this would be
					* the second \n to denote the end of headers*/
					currentSate = S_HEADERS_ALMOST_DONE;
					goto REEXECUTE;
				}

				c = token(ch);

				if (!c) {
					m_data.m_httpErrno = HPE_INVALID_HEADER_TOKEN;
					goto ERR;
				}

				if (!headerFieldMark) {
					headerFieldMark = p;
				}

				m_data.m_index = 0;
				currentSate = S_HEADER_FIELD;

				switch (c) {
				case 'c':
					m_data.m_headerState = H_C;
					break;

				case 'p':
					m_data.m_headerState = H_MATCHING_PROXY_CONNECTION;
					break;

				case 't':
					m_data.m_headerState = H_MATCHING_TRANSFER_ENCODING;
					break;

				case 'u':
					m_data.m_headerState = H_MATCHING_UPGRADE;
					break;

				default:
					m_data.m_headerState = H_GENERAL;
					break;
				}
			}
			break;

		case S_HEADER_FIELD: {
				const uint8* start = p;
				for (; p != data + len; p++) {
					ch = *p;
					c = token(ch);

					if (!c) {
						break;
					}

					switch (m_data.m_headerState) {
					case H_GENERAL:
						break;

					case H_C:
						m_data.m_index ++;
						m_data.m_headerState = (c == 'o' ? H_CO : H_GENERAL);
						break;

					case H_CO:
						m_data.m_index ++;
						m_data.m_headerState = (c == 'n' ? H_CON : H_GENERAL);
						break;

					case H_CON:
						m_data.m_index ++;
						switch (c) {
						case 'n':
							m_data.m_headerState = H_MATCHING_CONNECTION;
							break;
						case 't':
							m_data.m_headerState = H_MATCHING_CONTENT_LENGTH;
							break;
						default:
							m_data.m_headerState = H_GENERAL;
							break;
						}
						break;

					/* connection */
					case H_MATCHING_CONNECTION:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_CONNECTION) - 1
							|| c != s_CONNECTION[m_data.m_index]) {
							m_data.m_headerState = H_GENERAL;
						} else if (m_data.m_index == sizeof(s_CONNECTION) - 2) {
							m_data.m_headerState = H_CONNECTION;
						}
						break;

					/* proxy-connection */
					case H_MATCHING_PROXY_CONNECTION:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_PROXY_CONNECTION) - 1
							|| c != s_PROXY_CONNECTION[m_data.m_index]) {
							m_data.m_headerState = H_GENERAL;
						} else if (m_data.m_index == sizeof(s_PROXY_CONNECTION) - 2) {
							m_data.m_headerState = H_CONNECTION;
						}
						break;

					/* content-length */
					case H_MATCHING_CONTENT_LENGTH:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_CONTENT_LENGTH) - 1
							|| c != s_CONTENT_LENGTH[m_data.m_index]) {
							m_data.m_headerState = H_GENERAL;
						} else if (m_data.m_index == sizeof(s_CONTENT_LENGTH) - 2) {
							m_data.m_headerState = H_CONTENT_LENGTH;
						}
						break;

					/* transfer-encoding */
					case H_MATCHING_TRANSFER_ENCODING:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_TRANSFER_ENCODING) - 1
							|| c != s_TRANSFER_ENCODING[m_data.m_index]) {
							m_data.m_headerState = H_GENERAL;
						} else if (m_data.m_index == sizeof(s_TRANSFER_ENCODING) - 2) {
							m_data.m_headerState = H_TRANSFER_ENCODING;
						}
						break;

					/* upgrade */
					case H_MATCHING_UPGRADE:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_UPGRADE) - 1
							|| c != s_UPGRADE[m_data.m_index]) {
							m_data.m_headerState = H_GENERAL;
						} else if (m_data.m_index == sizeof(s_UPGRADE) - 2) {
							m_data.m_headerState = H_UPGRADE;
						}
						break;

					case H_CONNECTION:
					case H_CONTENT_LENGTH:
					case H_TRANSFER_ENCODING:
					case H_UPGRADE:
						if (ch != ' ') {
							m_data.m_headerState = H_GENERAL;
						}
						break;

					default:
						break;
					}
				}

				m_data.m_nread += p -start;
				if (m_data.m_nread > HTTP_MAX_HEADER_SIZE) {
					m_data.m_httpErrno = HPE_HEADER_OVERFLOW;
					goto ERR;
				}

				if (p == data + len) {
					-- p;
					break;
				}

				if (ch == ':') {
					currentSate = S_HEADER_VALUE_DISCARD_WS;

					if (m_data.m_httpErrno != HPE_OK) {
						DUYE_ERROR("m_data.m_httpErrno == HPE_OK");
						goto ERR;
					}

					if (headerFieldMark) {
						HttpParserListenerLst::iterator iter = m_listenerLst.begin();
						for (; iter != m_listenerLst.end(); iter++) {
							if (*iter == NULL) {
								DUYE_ERROR("*iter == NULL");
								goto ERR;
							}

							if ((*iter)->onHeaderField(&m_data, headerFieldMark, p - headerFieldMark) != 0) {
								m_data.m_httpErrno = HPE_L_HEADER_FIELD;
							}

							if (m_data.m_httpErrno != HPE_OK) {
								return (p - data + 1);
							}
						}
						headerFieldMark = NULL;
					}

					break;
				}

				m_data.m_httpErrno = HPE_INVALID_HEADER_TOKEN;
				goto ERR;
			}
			break;

		case S_HEADER_VALUE_DISCARD_WS:
			if (ch == ' ' || ch == '\t') {
				break;
			}

			if (ch == CR) {
				currentSate = S_HEADER_VALUE_DISCARD_WS_ALMOST_DONE;
				break;
			}

			if (ch == LF) {
				currentSate = S_HEADER_VALUE_DISCARD_LWS;
				break;
			}

		/* FALLTHROUGH */
		case S_HEADER_VALUE_START: {
				if (!headerValueMark) {
					headerValueMark = p;
				}

				currentSate = S_HEADER_VALUE;
				m_data.m_index = 0;

				c = HttpParser::lower(ch);

				switch (m_data.m_headerState) {
				case H_UPGRADE:
					m_data.m_flags |= HPF_UPGRADE;
					m_data.m_headerState = H_GENERAL;
					break;

				case H_TRANSFER_ENCODING:
					/* looking for 'Transfer-Encoding: chunked' */
					if ('c' == c) {
						m_data.m_headerState = H_MATCHING_TRANSFER_ENCODING_CHUNKED;
					} else {
						m_data.m_headerState = H_GENERAL;
					}
					break;

				case H_CONTENT_LENGTH:
					if (!isdigit(ch)) {
						m_data.m_httpErrno = HPE_INVALID_CONTENT_LENGTH;
						goto ERR;
					}

					m_data.m_contentLength = ch - '0';
					break;

				case H_CONNECTION:
					/* looking for 'Connection: keep-alive' */
					if (c == 'k') {
						m_data.m_headerState = H_MATCHING_CONNECTION_KEEP_ALIVE;
						
					} else if (c == 'c') {
						/* looking for 'Connection: close' */
						m_data.m_headerState = H_MATCHING_CONNECTION_CLOSE;
					} else if (c == 'u') {
						m_data.m_headerState = H_MATCHING_CONNECTION_UPGRADE;
					} else {
						m_data.m_headerState = H_MATCHING_CONNECTION_TOKEN;
					}
					break;

				/* Multi-value `Connection` header */
				case H_MATCHING_CONNECTION_TOKEN_START:
					break;

				default:
					m_data.m_headerState = H_GENERAL;
					break;
				}
			}
			break;

		case S_HEADER_VALUE: {
				const uint8* start = p;
				HeaderStates headerState = static_cast<HeaderStates>(m_data.m_headerState);
				for (; p != data + len; p++) {
					ch = *p;
					if (ch == CR) {
						currentSate = S_HEADER_ALMOST_DONE;
						m_data.m_headerState = headerState;

						//CALLBACK_DATA(header_value);
						if (m_data.m_httpErrno != HPE_OK) {
							DUYE_ERROR("m_data.m_httpErrno != HPE_OK");
							goto ERR;
						}

						if (headerValueMark) {
							HttpParserListenerLst::iterator iter = m_listenerLst.begin();
							for (; iter != m_listenerLst.end(); iter++) {
								if (*iter == NULL) {
									DUYE_ERROR("*iter == NULL");
									goto ERR;
								}

								if ((*iter)->onHeaderValue(&m_data, headerValueMark, p - headerValueMark) != 0) {
									m_data.m_httpErrno = HPE_L_HEADER_VALUE;
								}

								if (m_data.m_httpErrno != HPE_OK) {
									return (p - data + 1);
								}
							}
							headerValueMark = NULL;
						}

						break;
					}

					if (ch == LF) {
						currentSate = S_HEADER_ALMOST_DONE;

						m_data.m_nread += p - start;
						if (m_data.m_nread > HTTP_MAX_HEADER_SIZE) {
							m_data.m_httpErrno = HPE_HEADER_OVERFLOW;
							goto ERR;
						}
						m_data.m_headerState = headerState;

						//CALLBACK_DATA_NOADVANCE(header_value);
						if (m_data.m_httpErrno != HPE_OK) goto ERR;
						if (headerValueMark) {
							HttpParserListenerLst::iterator iter = m_listenerLst.begin();
							for (; iter != m_listenerLst.end(); iter++) {
								if (*iter == NULL) {
									DUYE_ERROR("*iter == NULL");
									goto ERR;
								}

								if ((*iter)->onHeaderValue(&m_data, headerValueMark, p - headerValueMark) != 0) {
									m_data.m_httpErrno = HPE_L_HEADER_VALUE;
								}

								if (m_data.m_httpErrno != HPE_OK) {
									return (p - data);
								}
							}
							headerValueMark = NULL;
						}

						goto REEXECUTE;
					}

					c = lower(ch);

					switch (headerState) {
					case H_GENERAL: {
							const uint8* pCr;
							const uint8* pLf;
							uint32 limit = data + len - p;

							limit = limit < HTTP_MAX_HEADER_SIZE ? limit : HTTP_MAX_HEADER_SIZE;

							pCr = (const uint8*) memchr(p, CR, limit);
							pLf = (const uint8*) memchr(p, LF, limit);
							if (pCr != NULL) {
								if (pLf != NULL && pCr >= pLf) {
									p = pLf;
								} else {
									p = pCr;
								}
							} else if (pLf != NULL) {
								p = pLf;
							} else {
								p = data + len;
							}
							-- p;
						}
						break;

					case H_CONNECTION:
					case H_TRANSFER_ENCODING:
						break;

					case H_CONTENT_LENGTH: {
							uint64 t;

							if (ch == ' ') {
								break;
							}

							if (!isdigit(ch)) {
								m_data.m_httpErrno = HPE_INVALID_CONTENT_LENGTH;
								m_data.m_headerState = headerState;
								goto ERR;
							}

							t = m_data.m_contentLength;
							t *= 10;
							t += ch - '0';

							/* Overflow? Test against a conservative limit for simplicity. */
							if ((ULLONG_MAX - 10) / 10 < m_data.m_contentLength) {
								m_data.m_httpErrno = HPE_INVALID_CONTENT_LENGTH;
								m_data.m_headerState = headerState;
								goto ERR;
							}

							m_data.m_contentLength = t;
						}
						break;

					/* Transfer-Encoding: chunked */
					case H_MATCHING_TRANSFER_ENCODING_CHUNKED:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_CHUNKED) - 1
							|| c != s_CHUNKED[m_data.m_index]) {
								headerState = H_GENERAL;
						} else if (m_data.m_index == sizeof(s_CHUNKED) - 2) {
							headerState = H_TRANSFER_ENCODING_CHUNKED;
						}
						break;

					case H_MATCHING_CONNECTION_TOKEN_START:
						/* looking for 'Connection: keep-alive' */
						if (c == 'k') {
							headerState = H_MATCHING_CONNECTION_KEEP_ALIVE;
							
						} else if (c == 'c') {	
							/* looking for 'Connection: close' */
							headerState = H_MATCHING_CONNECTION_CLOSE;
						} else if (c == 'u') {
							headerState = H_MATCHING_CONNECTION_UPGRADE;
						} else if (HttpParser::strcitToken(c)) {
							headerState = H_MATCHING_CONNECTION_TOKEN;
						} else if (c == ' ' || c == '\t') {
							/* Skip lws */
						} else {
							headerState = H_GENERAL;
						}
						break;

					/* looking for 'Connection: keep-alive' */
					case H_MATCHING_CONNECTION_KEEP_ALIVE:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_KEEP_ALIVE) - 1
							|| c != s_KEEP_ALIVE[m_data.m_index]) 
						{
							headerState = H_MATCHING_CONNECTION_TOKEN;
						} 
						else if (m_data.m_index == sizeof(s_KEEP_ALIVE) - 2)
						{
							headerState = H_CONNECTION_KEEP_ALIVE;
						}
						break;

					/* looking for 'Connection: close' */
					case H_MATCHING_CONNECTION_CLOSE:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_CLOSE) - 1 ||
							c != s_CLOSE[m_data.m_index]) 
						{
							headerState = H_MATCHING_CONNECTION_TOKEN;
						}
						else if (m_data.m_index == sizeof(s_CLOSE) - 2) 
						{
							headerState = H_CONNECTION_CLOSE;
						}
						break;

					/* looking for 'Connection: upgrade' */
					case H_MATCHING_CONNECTION_UPGRADE:
						m_data.m_index ++;
						if (m_data.m_index > sizeof(s_UPGRADE) - 1 ||
							c != s_UPGRADE[m_data.m_index]) 
						{
							headerState = H_MATCHING_CONNECTION_TOKEN;
						} 
						else if (m_data.m_index == sizeof(s_UPGRADE) - 2) 
						{
							headerState = H_CONNECTION_UPGRADE;
						}
						break;

					case H_MATCHING_CONNECTION_TOKEN:
						if (ch == ',') 
						{
							headerState = H_MATCHING_CONNECTION_TOKEN_START;
							m_data.m_index = 0;
						}
						break;

					case H_TRANSFER_ENCODING_CHUNKED:
						if (ch != ' ') 
							headerState = H_GENERAL;
						break;

					case H_CONNECTION_KEEP_ALIVE:
					case H_CONNECTION_CLOSE:
					case H_CONNECTION_UPGRADE:
						if (ch == ',') 
						{
							if (headerState == H_CONNECTION_KEEP_ALIVE) 
							{
								m_data.m_flags |= HPF_CONNECTION_KEEP_ALIVE;
							} 
							else if (headerState == H_CONNECTION_CLOSE) 
							{
								m_data.m_flags |= HPF_CONNECTION_CLOSE;
							} 
							else if (headerState == H_CONNECTION_UPGRADE) 
							{
								m_data.m_flags |= HPF_CONNECTION_UPGRADE;
							}
							headerState = H_MATCHING_CONNECTION_TOKEN_START;
							m_data.m_index = 0;
						} 
						else if (ch != ' ') 
						{
							headerState = H_MATCHING_CONNECTION_TOKEN;
						}
						break;

					default:
						currentSate = S_HEADER_VALUE;
						headerState = H_GENERAL;
						break;
					}
				}
				m_data.m_headerState = headerState;

				m_data.m_nread += p - start;
				if (m_data.m_nread > HTTP_MAX_HEADER_SIZE)
				{
					m_data.m_httpErrno = HPE_HEADER_OVERFLOW;
					goto ERR;
				}

				if (p == data + len)
					-- p;
			}
			break;

		case S_HEADER_ALMOST_DONE:
			{
				if (HttpParser::strictCheck(ch != LF))
				{
					m_data.m_httpErrno = HPE_STRICT;
					goto ERR;
				}

				currentSate = S_HEADER_VALUE_LWS;
			}
			break;

		case S_HEADER_VALUE_LWS:
			{
				if (ch == ' ' || ch == '\t') 
				{
					currentSate = S_HEADER_VALUE_START;
					goto REEXECUTE;
				}

				/* finished the header */
				switch (m_data.m_headerState)
				{
				case H_CONNECTION_KEEP_ALIVE:
					m_data.m_flags |= HPF_CONNECTION_KEEP_ALIVE;
					break;
				case H_CONNECTION_CLOSE:
					m_data.m_flags |= HPF_CONNECTION_CLOSE;
					break;
				case H_TRANSFER_ENCODING_CHUNKED:
					m_data.m_flags |= HPF_CHUNKED;
					break;
				case H_CONNECTION_UPGRADE:
					m_data.m_flags |= HPF_CONNECTION_UPGRADE;
					break;
				default:
					break;
				}

				currentSate = S_HEADER_FIELD_START;
				goto REEXECUTE;
			}
			break;

		case S_HEADER_VALUE_DISCARD_WS_ALMOST_DONE:
			{
				if (HttpParser::strictCheck(ch != LF))
				{
					m_data.m_httpErrno = HPE_STRICT;
					goto ERR;
				}

				currentSate = S_HEADER_VALUE_DISCARD_LWS;
			}
			break;

		case S_HEADER_VALUE_DISCARD_LWS:
			{
				if (ch == ' ' || ch == '\t')
				{
					currentSate = S_HEADER_VALUE_DISCARD_WS;
					break;
				} 
				else
				{
					switch (m_data.m_headerState) 
					{
					case H_CONNECTION_KEEP_ALIVE:
						m_data.m_flags |= HPF_CONNECTION_KEEP_ALIVE;
						break;
					case H_CONNECTION_CLOSE:
						m_data.m_flags |= HPF_CONNECTION_CLOSE;
						break;
					case H_CONNECTION_UPGRADE:
						m_data.m_flags |= HPF_CONNECTION_UPGRADE;
						break;
					case H_TRANSFER_ENCODING_CHUNKED:
						m_data.m_flags |= HPF_CHUNKED;
						break;
					default:
						break;
					}

					/* header value was empty */
					if (!headerValueMark)
					{
						headerValueMark = p;
					}
					currentSate = S_HEADER_FIELD_START;

					//CALLBACK_DATA_NOADVANCE(header_value);
					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					if (headerValueMark)
					{
						HttpParserListenerLst::iterator iter = m_listenerLst.begin();
						for (; iter != m_listenerLst.end(); ++iter)
						{
							if (*iter == NULL) continue;
							if ((*iter)->onHeaderValue(&m_data, headerValueMark, p - headerValueMark) != 0)
								m_data.m_httpErrno = HPE_L_HEADER_VALUE;

							if (m_data.m_httpErrno != HPE_OK)
								return (p - data);
						}
						headerValueMark = NULL;
					}

					goto REEXECUTE;
				}
			}
			break;

		case S_HEADERS_ALMOST_DONE:
			{
				if (HttpParser::strictCheck(ch != LF))
				{
					m_data.m_httpErrno = HPE_STRICT;
					goto ERR;
				}

				if (m_data.m_flags & HPF_TRAILING) 
				{
					/* End of a chunked request */
					currentSate = newMessage(m_data);

					//CALLBACK_NOTIFY(message_complete);
					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					HttpParserListenerLst::iterator iter = m_listenerLst.begin();
					for (; iter != m_listenerLst.end(); ++iter)
					{
						if (*iter == NULL) continue;
						if ((*iter)->onMessageComplete(&m_data) != 0)
							m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

						if (m_data.m_httpErrno != HPE_OK)
							return (p - data + 1);
					}

					break;
				}

				currentSate = S_HEADERS_DONE;

				/* Set this here so that onHeadersComplete() can see it */
				m_data.m_upgrade =
					((m_data.m_flags & (HPF_UPGRADE | HPF_CONNECTION_UPGRADE)) ==
					(HPF_UPGRADE | HPF_CONNECTION_UPGRADE) ||
					m_data.m_method == HTTP_CONNECT);

				/* Here we call the headers_complete callback. This is somewhat
				* different than other callbacks because if the user returns 1, we
				* will interpret that as saying that this message has no body. This
				* is needed for the annoying case of recieving a response to a HEAD
				* request.
				*/
				HttpParserListenerLst::iterator iter = m_listenerLst.begin();
				for (; iter != m_listenerLst.end(); ++iter)
				{
					if (*iter == NULL) continue;
					switch ((*iter)->onHeadersComplete(&m_data))
					{
					case 0:
						break;

					case 1:
						m_data.m_flags |= HPF_SKIPBODY;
						break;

					default:
						m_data.m_httpErrno = HPE_L_HEADERS_COMPLETE;
						//RETURN(p - data); /* Error */
						m_data.m_state = currentSate;
						return p - data;
					}
				}
				
				if (m_data.m_httpErrno != HPE_OK) 
				{
					//RETURN(p - data);
					m_data.m_state = currentSate;
					return p - data;
				}

				goto REEXECUTE;
			}
			break;

		case S_HEADERS_DONE:
			{
				if (HttpParser::strictCheck(ch != LF))
				{
					m_data.m_httpErrno = HPE_STRICT;
					goto ERR;
				}

				m_data.m_nread = 0;

				/* Exit, the rest of the connect is in a different protocol. */
				if (m_data.m_upgrade)
				{
					currentSate = newMessage(m_data);

					//CALLBACK_NOTIFY(message_complete);
					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					HttpParserListenerLst::iterator iter = m_listenerLst.begin();
					for (; iter != m_listenerLst.end(); ++iter)
					{
						if (*iter == NULL) continue;
						if ((*iter)->onMessageComplete(&m_data) != 0)
							m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

						if (m_data.m_httpErrno != HPE_OK)
							return (p - data + 1);
					}

					//RETURN((p - data) + 1);
					m_data.m_state = currentSate;
					return p - data + 1;
				}

				if (m_data.m_flags & HPF_SKIPBODY) 
				{
					currentSate = newMessage(m_data);

					//CALLBACK_NOTIFY(message_complete);
					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					HttpParserListenerLst::iterator iter = m_listenerLst.begin();
					for (; iter != m_listenerLst.end(); ++iter)
					{
						if (*iter == NULL) continue;
						if ((*iter)->onMessageComplete(&m_data) != 0)
							m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

						if (m_data.m_httpErrno != HPE_OK)
							return (p - data + 1);
					}
				} 
				else if (m_data.m_flags & HPF_CHUNKED) 
				{
					/* chunked encoding - ignore Content-Length header */
					currentSate = S_CHUNK_SIZE_START;
				} 
				else 
				{
					if (m_data.m_contentLength == 0) 
					{
						/* Content-Length header given but zero: Content-Length: 0\r\n */
						currentSate = newMessage(m_data);

						//CALLBACK_NOTIFY(message_complete);
						if (m_data.m_httpErrno != HPE_OK) goto ERR;
						HttpParserListenerLst::iterator iter = m_listenerLst.begin();
						for (; iter != m_listenerLst.end(); ++iter)
						{
							if (*iter == NULL) continue;
							if ((*iter)->onMessageComplete(&m_data) != 0)
								m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

							if (m_data.m_httpErrno != HPE_OK)
								return (p - data + 1);
						}
					} 
					else if (m_data.m_contentLength != ULLONG_MAX) 
					{
						/* Content-Length header given and non-zero */
						currentSate = S_BODY_IDENTITY;
					} 
					else
					{
						if (m_data.m_type == HPT_REQUEST ||
							!isNeedEof(m_data))
						{
							/* Assume content-length 0 - read the next */
							currentSate = newMessage(m_data);
							
							//CALLBACK_NOTIFY(message_complete);
							if (m_data.m_httpErrno != HPE_OK) goto ERR;
							HttpParserListenerLst::iterator iter = m_listenerLst.begin();
							for (; iter != m_listenerLst.end(); ++iter)
							{
								if (*iter == NULL) continue;
								if ((*iter)->onMessageComplete(&m_data) != 0)
									m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

								if (m_data.m_httpErrno != HPE_OK)
									return (p - data + 1);
							}
						}
						else
						{
							/* Read body until EOF */
							currentSate = S_BODY_IDENTITY_EOF;
						}
					}
				}
			}
			break;

		case S_BODY_IDENTITY:
			{
				uint64 toRead = m_data.m_contentLength < (uint64) ((data + len) - p) ? 
					m_data.m_contentLength :(uint64) ((data + len) - p);

				if (m_data.m_contentLength == 0 || m_data.m_contentLength != ULLONG_MAX) {
					goto ERR;
				}

				/* The difference between advancing content_length and p is because
				* the latter will automaticaly advance on the next loop iteration.
				* Further, if content_length ends up at 0, we want to see the last
				* byte again for our message complete callback.
				*/
				if (!bodyMark)
				{
					bodyMark = p;
				}
				m_data.m_contentLength -= toRead;
				p += toRead - 1;

				if (m_data.m_contentLength == 0)
				{
					currentSate = S_MESSAGE_DONE;

					/* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
					*
					* The alternative to doing this is to wait for the next byte to
					* trigger the data callback, just as in every other case. The
					* problem with this is that this makes it difficult for the test
					* harness to distinguish between complete-on-EOF and
					* complete-on-length. It's not clear that this distinction is
					* important for applications, but let's keep it for now.
					*/
					//CALLBACK_DATA_(body, p - body_mark + 1, p - data);
					if (m_data.m_httpErrno != HPE_OK) goto ERR;
					if (bodyMark)
					{
						HttpParserListenerLst::iterator iter = m_listenerLst.begin();
						for (; iter != m_listenerLst.end(); ++iter)
						{
							if (*iter == NULL) continue;
							if ((*iter)->onBody(&m_data, bodyMark, p - bodyMark + 1) != 0)
								m_data.m_httpErrno = HPE_L_BODY;

							if (m_data.m_httpErrno != HPE_OK)
								return (p - data);
						}
						bodyMark = NULL;
					}

					goto REEXECUTE;
				}
			}
			break;

		/* read until EOF */
		case S_BODY_IDENTITY_EOF:
			if (!bodyMark)
			{
				bodyMark = p;
			}
			p = data + len - 1;

			break;

		case S_MESSAGE_DONE:
			{
				currentSate = newMessage(m_data);
			
				//CALLBACK_NOTIFY(message_complete);
				if (m_data.m_httpErrno != HPE_OK) goto ERR;
				HttpParserListenerLst::iterator iter = m_listenerLst.begin();
				for (; iter != m_listenerLst.end(); ++iter)
				{
					if (*iter == NULL) continue;
					if ((*iter)->onMessageComplete(&m_data) != 0)
						m_data.m_httpErrno = HPE_L_MESSAGE_COMPLETE;

					if (m_data.m_httpErrno != HPE_OK)
						return (p - data + 1);
				}
			}
			break;

		case S_CHUNK_SIZE_START:
			{
				if (m_data.m_nread != 1) goto ERR;
				if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;

				unhexVal = s_unhex[(uint8)ch];
				if (unhexVal == -1)
				{
					m_data.m_httpErrno = HPE_INVALID_CHUNK_SIZE;
					goto ERR;
				}

				m_data.m_contentLength = unhexVal;
				currentSate = S_CHUNK_SIZE;				
			}
			break;

		case S_CHUNK_SIZE:
			{
				uint64 t;

				if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;

				if (ch == CR) 
				{
					currentSate = S_CHUNK_SIZE_ALMOST_DONE;
					break;
				}

				unhexVal = s_unhex[(unsigned char)ch];

				if (unhexVal == -1) 
				{
					if (ch == ';' || ch == ' ') 
					{
						currentSate = S_CHUNK_PARAMETERS;
						break;
					}

					m_data.m_httpErrno = HPE_INVALID_CHUNK_SIZE;
					goto ERR;
				}

				t = m_data.m_contentLength;
				t *= 16;
				t += unhexVal;

				/* Overflow? Test against a conservative limit for simplicity. */
				if ((ULLONG_MAX - 16) / 16 < m_data.m_contentLength)
				{
					m_data.m_httpErrno = HPE_INVALID_CONTENT_LENGTH;
					goto ERR;
				}

				m_data.m_contentLength = t;
			}
			break;

		case S_CHUNK_PARAMETERS:
			{
				if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;

				/* just ignore this shit. TODO check for overflow */
				if (ch == CR)
				{
					currentSate = S_CHUNK_SIZE_ALMOST_DONE;
					break;
				}
			}
			break;

		case S_CHUNK_SIZE_ALMOST_DONE:
			{
				if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;
				
				if (HttpParser::strictCheck(ch != LF))
				{
					m_data.m_httpErrno = HPE_STRICT;
					goto ERR;
				}

				m_data.m_nread = 0;

				if (m_data.m_contentLength == 0)
				{
					m_data.m_flags |= HPF_TRAILING;
					currentSate = S_HEADER_FIELD_START;
				} 
				else 
				{
					currentSate = S_CHUNK_DATA;
				}
			}
			break;

		case S_CHUNK_DATA:
			{
				uint64 toRead = m_data.m_contentLength < (uint64)(data + len - p) ?
					m_data.m_contentLength : (uint64)(data + len - p);

				if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;
				if (m_data.m_contentLength == 0 || m_data.m_contentLength != ULLONG_MAX) {
					goto ERR;
				}

				/* See the explanation in s_body_identity for why the content
				* length and data pointers are managed this way.
				*/
				if (!bodyMark)
				{
					bodyMark = p;
				}
				m_data.m_contentLength -= toRead;
				p += toRead - 1;

				if (m_data.m_contentLength == 0) 
				{
					currentSate = S_CHUNK_DATA_ALMOST_DONE;
				}
			}
			break;

		case S_CHUNK_DATA_ALMOST_DONE:
			if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;
			if (m_data.m_contentLength != 0) goto ERR;
			if (HttpParser::strictCheck(ch != CR))
			{
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			currentSate = S_CHUNK_DATA_DONE;
			
			//CALLBACK_DATA(body);
			if (m_data.m_httpErrno != HPE_OK) goto ERR;
			if (bodyMark)
			{
				HttpParserListenerLst::iterator iter = m_listenerLst.begin();
				for (; iter != m_listenerLst.end(); ++iter)
				{
					if (*iter == NULL) continue;
					if ((*iter)->onBody(&m_data, bodyMark, p - bodyMark) != 0)
						m_data.m_httpErrno = HPE_L_BODY;

					if (m_data.m_httpErrno != HPE_OK)
						return (p - data + 1);
				}
				bodyMark = NULL;
			}
			
			break;

		case S_CHUNK_DATA_DONE:
			if (!(m_data.m_flags & HPF_CHUNKED)) goto ERR;
			if (HttpParser::strictCheck(ch != LF))
			{
				m_data.m_httpErrno = HPE_STRICT;
				goto ERR;
			}
			m_data.m_nread = 0;
			currentSate = S_CHUNK_SIZE_START;
			break;

		default:
			// assert(0 && "unhandled state");
			m_data.m_httpErrno = HPE_INVALID_INTERNAL_STATE;
			goto ERR;
		}
	}

	/* Run callbacks for any marks that we have leftover after we ran our of
	* bytes. There should be at most one of these set, so it's OK to invoke
	* them in series (unset marks will not result in callbacks).
	*
	* We use the NOADVANCE() variety of callbacks here because 'p' has already
	* overflowed 'data' and this allows us to correct for the off-by-one that
	* we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
	* value that's in-bounds).
	*/
	if (((headerFieldMark ? 1 : 0) +
		(headerValueMark ? 1 : 0) +
		(urlMark ? 1 : 0)  +
		(bodyMark ? 1 : 0) +
		(statusMark ? 1 : 0)) > 1) {
		goto ERR;
	}

	//CALLBACK_DATA_NOADVANCE(header_field);
	if (m_data.m_httpErrno != HPE_OK) goto ERR;
	if (headerFieldMark)
	{
		HttpParserListenerLst::iterator iter = m_listenerLst.begin();
		for (; iter != m_listenerLst.end(); ++iter)
		{
			if (*iter == NULL) continue;
			if ((*iter)->onHeaderField(&m_data, headerFieldMark, p - headerFieldMark) != 0)
				m_data.m_httpErrno = HPE_L_HEADER_FIELD;

			if (m_data.m_httpErrno != HPE_OK)
				return (p - data);
		}
		headerFieldMark = NULL;
	}
	//CALLBACK_DATA_NOADVANCE(header_value);
	if (m_data.m_httpErrno != HPE_OK) goto ERR;
	if (headerValueMark)
	{
		HttpParserListenerLst::iterator iter = m_listenerLst.begin();
		for (; iter != m_listenerLst.end(); ++iter)
		{
			if (*iter == NULL) continue;
			if ((*iter)->onHeaderValue(&m_data, headerValueMark, p - headerValueMark) != 0)
				m_data.m_httpErrno = HPE_L_HEADER_VALUE;

			if (m_data.m_httpErrno != HPE_OK)
				return (p - data);
		}
		headerValueMark = NULL;
	}
	//CALLBACK_DATA_NOADVANCE(url);
	if (m_data.m_httpErrno != HPE_OK) goto ERR;
	if (urlMark)
	{
		HttpParserListenerLst::iterator iter = m_listenerLst.begin();
		for (; iter != m_listenerLst.end(); ++iter)
		{
			if (*iter == NULL) continue;
			if ((*iter)->onUrl(&m_data, urlMark, p - urlMark) != 0)
				m_data.m_httpErrno = HPE_L_URL;

			if (m_data.m_httpErrno != HPE_OK)
				return (p - data);
		}
		urlMark = NULL;
	}
	//CALLBACK_DATA_NOADVANCE(body);
	if (m_data.m_httpErrno != HPE_OK) goto ERR;
	if (bodyMark)
	{
		HttpParserListenerLst::iterator iter = m_listenerLst.begin();
		for (; iter != m_listenerLst.end(); ++iter)
		{
			if (*iter == NULL) continue;
			if ((*iter)->onBody(&m_data, bodyMark, p - bodyMark) != 0)
				m_data.m_httpErrno = HPE_L_BODY;

			if (m_data.m_httpErrno != HPE_OK)
				return (p - data);
		}
		bodyMark = NULL;
	}
	//CALLBACK_DATA_NOADVANCE(status);
	if (m_data.m_httpErrno != HPE_OK) goto ERR;
	if (statusMark)
	{
		HttpParserListenerLst::iterator iter = m_listenerLst.begin();
		for (; iter != m_listenerLst.end(); ++iter)
		{
			if (*iter == NULL) continue;
			if ((*iter)->onStatus(&m_data, statusMark, p - statusMark) != 0)
				m_data.m_httpErrno = HPE_L_STATUS;

			if (m_data.m_httpErrno != HPE_OK)
				return (p - data);
		}
		statusMark = NULL;
	}

	m_data.m_state = currentSate;
	return len;

ERR:
	if (m_data.m_httpErrno == HPE_OK)
		m_data.m_httpErrno = HPE_UNKNOWN;

	return 0;
}

void HttpParser::reset(HttpParserType type, void* data)
{
	memset(&m_data, 0, sizeof(m_data));
	m_data.m_type = type;
	m_data.m_state = (type == HPT_REQUEST ? S_START_REQ :
		(type == HPT_RESPONSE ? S_START_RES : S_START_REQ_OR_RES));
	m_data.m_httpErrno = HPE_OK;
	m_data.m_data = data;
}

HttpParser::ParserState HttpParser::parseUrlChar(HttpParser::ParserState s, const char ch)
{
	DUYE_DEBUG(" ParserState = %d, char = %c(%d)", s, ch, ch);
	if (ch == ' ' || ch == '\r' || ch == '\n') 
	{
		return S_DEAD;
	}

#if HTTP_PARSER_STRICT
	if (ch == '\t' || ch == '\f') 
	{
		return S_DEAD;
	}
#endif

	switch (s) 
	{
	case S_REQ_SPACES_BEFORE_URL:
		/* Proxied requests are followed by scheme of an absolute URI (alpha).
		* All methods except CONNECT are followed by '/' or '*'.
		*/
		if (ch == '/' || ch == '*')
		{
			return S_REQ_PATH;
		}

		if (isalpha(ch)) 
		{
			return S_REQ_SCHEMA;
		}

		break;

	case S_REQ_SCHEMA:
		if (isalpha(ch))
		{
			return s;
		}

		if (ch == ':') {
			return S_REQ_SCHEMA_SLASH;
		}

		break;

	case S_REQ_SCHEMA_SLASH:
		if (ch == '/') 
		{
			return S_REQ_SCHEMA_SLASH_SLASH;
		}

		break;

	case S_REQ_SCHEMA_SLASH_SLASH:
		if (ch == '/') 
		{
			return S_REQ_SERVER_START;
		}

		break;

	case S_REQ_SERVER_WITH_AT:
		if (ch == '@')
		{
			return S_DEAD;
		}

		/* FALLTHROUGH */
	case S_REQ_SERVER_START:
	case S_REQ_SERVER:
		if (ch == '/') 
		{
			return S_REQ_PATH;
		}

		if (ch == '?') 
		{
			return S_REQ_QUERY_STRING_START;
		}

		if (ch == '@') 
		{
			return S_REQ_SERVER_WITH_AT;
		}

		/* user info char */
		if (HttpParser::isUserInfoChar(ch))
		{
			return S_REQ_SERVER;
		}

		break;

	case S_REQ_PATH:
		if (HttpParser::isUrlChar(ch)) 
		{
			return s;
		}

		switch (ch) 
		{
		case '?':
			return S_REQ_QUERY_STRING_START;

		case '#':
			return S_REQ_FRAGMENT_START;
		}

		break;

	case S_REQ_QUERY_STRING_START:
	case S_REQ_QUERY_STRING:
		if (HttpParser::isUrlChar(ch))
		{
			return S_REQ_QUERY_STRING;
		}

		switch (ch)
		{
		case '?':
			/* allow extra '?' in query string */
			return S_REQ_QUERY_STRING;

		case '#':
			return S_REQ_FRAGMENT_START;
		}

		break;

	case S_REQ_FRAGMENT_START:
		if (HttpParser::isUrlChar(ch)) 
		{
			return S_REQ_FRAGMENT;
		}

		switch (ch)
		{
		case '?':
			return S_REQ_FRAGMENT;

		case '#':
			return s;
		}

		break;

	case S_REQ_FRAGMENT:
		if (HttpParser::isUrlChar(ch)) 
		{
			return s;
		}

		switch (ch) 
		{
		case '?':
		case '#':
			return s;
		}

		break;

	default:
		break;
	}

	/* We should never fall out of the switch above unless there's an error */
	return S_DEAD;
}

std::string HttpParser::getMethodStr(HttpMethod m)
{
	return s_methodString[m];
}

std::string HttpParser::getErrnoName(HttpParserErrno err)
{
	return std::string(s_httpParserStrErrorTable[err].name);
}

std::string HttpParser::getErrnoDescription(HttpParserErrno err)
{
	return std::string(s_httpParserStrErrorTable[err].description);
}

bool HttpParser::addListener(HttpParserListener* listener)
{
	if (!listener)
		return false;

	HttpParserListenerLst::iterator iter = m_listenerLst.begin();
	for (; iter != m_listenerLst.end(); ++iter)
	{
		if ((*iter) == listener)
			return false;
	}
	
	m_listenerLst.push_back(listener);
	return true;
}

void HttpParser::delListener(HttpParserListener* listener)
{
	HttpParserListenerLst::iterator iter = m_listenerLst.begin();
	for (; iter != m_listenerLst.end(); ++iter)
	{
		if ((*iter) == listener)
		{
			m_listenerLst.erase(iter);
			return;
		}
	}
}

bool HttpParser::strictCheck(bool b)
{
#if HTTP_PARSER_STRICT
	return b;
#else
	return false;
#endif
}

bool HttpParser::isUserInfoChar(uint8 ch)
{
	return (isalnum(ch) ||
		ch == '-' || ch == '_' || ch == '.' || ch == '!' ||
		ch == '~' || ch == '*' || ch == '\'' || ch == '(' || 
		ch == ')' || ch == '%' || ch == ';' || ch == ':' || 
		ch == '&' || ch == '=' || ch == '+' || ch == '$' ||
		ch == ',' || ch == '[' || ch == ']');
}

bool HttpParser::isUrlChar(uint8 ch)
{
#if HTTP_PARSER_STRICT
	return (((uint32)s_normalUrlChar[(uint32)ch >> 3] &  
		(1 << ((uint32)ch & 7)))) != 0;
#else
	return ((((uint32)s_normalUrlChar[(uint32)ch >> 3] &  
		(1 << ((uint32)ch & 7)))) || (ch & 0x80)) != 0;
#endif
}

bool HttpParser::isHostChar(uint8 ch)
{
#if HTTP_PARSER_STRICT
	return (isalnum(ch) || (ch) == '.' || (ch) == '-');
#else
	return (isalnum(ch) || (ch) == '.' || (ch) == '-' || (ch) == '_');
#endif
}

uint8 HttpParser::token(uint8 ch)
{
#if HTTP_PARSER_STRICT
	return s_tokens[ch];
#else
	return ((ch == ' ') ? ' ' : tokens[ch]);
#endif
}

uint8 HttpParser::strcitToken(uint8 ch)
{
	return s_tokens[ch];
}

uint8 HttpParser::lower(uint8 ch)
{
	return (ch | 0x20);
}

bool HttpParser::isKeepAlive(const HttpParserData& data)
{
	if (data.m_httpMajor > 0 && data.m_httpMinor > 0) 
	{
		/* HTTP/1.1 */
		if (data.m_flags & HPF_CONNECTION_CLOSE) {
			return false;
		}
	} 
	else
	{
		/* HTTP/1.0 or earlier */
		if (!(data.m_flags & HPF_CONNECTION_KEEP_ALIVE)) 
		{
			return false;
		}
	}

	return !isNeedEof(data);
}

/* Does the parser need to see an EOF to find the end of the message? */
bool HttpParser::isNeedEof(const HttpParserData& data)
{
	if (data.m_type == HPT_REQUEST) 
	{
		return false;
	}

	/* See RFC 2616 section 4.4 */
	if (data.m_statusCode / 100 == 1 || /* 1xx e.g. Continue */
		data.m_statusCode == 204 ||     /* No Content */
		data.m_statusCode == 304 ||     /* Not Modified */
		data.m_flags & HPF_SKIPBODY)     /* response to a HEAD request */
	{
		return false;
	}

	if ((data.m_flags & HPF_CHUNKED) || 
		data.m_contentLength != ULLONG_MAX)
	{
		return false;
	}

	return true;
}

HttpParser::ParserState HttpParser::newMessage(const HttpParserData& data)
{
#if HTTP_PARSER_STRICT
	if (isKeepAlive(data))
	{
#endif
		return data.m_type == HPT_REQUEST ? S_START_REQ : S_START_RES;
#if HTTP_PARSER_STRICT
	}
	return S_DEAD;
#endif
}

HttpParser::HttpHostState HttpParser::parseHostChar
	(
	HttpParser::HttpHostState s, 
	const uint8 ch
	)
{
	switch(s) 
	{
	case S_HTTP_USERINFO:
	case S_HTTP_USERINFO_START:
		if (ch == '@')
		{
			return S_HTTP_HOST_START;
		}

		if (HttpParser::isUserInfoChar(ch))
		{
			return S_HTTP_USERINFO;
		}

		break;

	case S_HTTP_HOST_START:
		if (ch == '[') 
		{
			return S_HTTP_HOST_V6_START;
		}

		if (HttpParser::isHostChar(ch)) 
		{
			return S_HTTP_HOST;
		}

		break;

	case S_HTTP_HOST:
		if (HttpParser::isHostChar(ch)) 
		{
			return S_HTTP_HOST;
		}

		/* FALLTHROUGH */
	case S_HTTP_HOST_V6_END:
		if (ch == ':') 
		{
			return S_HTTP_HOST_PORT_START;
		}

		break;

	case S_HTTP_HOST_V6:
		if (ch == ']') 
		{
			return S_HTTP_HOST_V6_END;
		}

		/* FALLTHROUGH */
	case S_HTTP_HOST_V6_START:
		if (isxdigit(ch) || ch == ':' || ch == '.') 
		{
			return S_HTTP_HOST_V6;
		}

		break;

	case S_HTTP_HOST_PORT:
	case S_HTTP_HOST_PORT_START:
		if (isdigit(ch))
		{
			return S_HTTP_HOST_PORT;
		}

		break;

	default:
		break;
	}
	return S_HTTP_HOST_DEAD;
}

int32 HttpParser::parseHost(const uint8* buf, HttpParserUrl& u, bool foundAt)
{
	HttpHostState s;

	const uint8* p;
	size_t buflen = u.fieldData[HPUF_HOST].off + u.fieldData[HPUF_HOST].len;

	u.fieldData[HPUF_HOST].len = 0;

	s = foundAt ? S_HTTP_USERINFO_START : S_HTTP_HOST_START;

	for (p = buf + u.fieldData[HPUF_HOST].off; p < buf + buflen; p ++) 
	{
		HttpHostState new_s = HttpParser::parseHostChar(s, *p);

		if (new_s == S_HTTP_HOST_DEAD) 
		{
			return 1;
		}

		switch(new_s) 
		{
		case S_HTTP_HOST:
			if (s != S_HTTP_HOST)
			{
				u.fieldData[HPUF_HOST].off = p - buf;
			}
			u.fieldData[HPUF_HOST].len ++;
			break;

		case S_HTTP_HOST_V6:
			if (s != S_HTTP_HOST_V6) 
			{
				u.fieldData[HPUF_HOST].off = p - buf;
			}
			u.fieldData[HPUF_HOST].len ++;
			break;

		case S_HTTP_HOST_PORT:
			if (s != S_HTTP_HOST_PORT)
			{
				u.fieldData[HPUF_PORT].off = p - buf;
				u.fieldData[HPUF_PORT].len = 0;
				u.fieldSet |= (1 << HPUF_PORT);
			}
			u.fieldData[HPUF_PORT].len ++;
			break;

		case S_HTTP_USERINFO:
			if (s != S_HTTP_USERINFO) {
				u.fieldData[HPUF_USERINFO].off = p - buf ;
				u.fieldData[HPUF_USERINFO].len = 0;
				u.fieldSet |= (1 << HPUF_USERINFO);
			}
			u.fieldData[HPUF_USERINFO].len ++;
			break;

		default:
			break;
		}
		s = new_s;
	}

	/* Make sure we don't end somewhere unexpected */
	switch (s) 
	{
	case S_HTTP_HOST_START:
	case S_HTTP_HOST_V6_START:
	case S_HTTP_HOST_V6:
	case S_HTTP_HOST_PORT_START:
	case S_HTTP_USERINFO:
	case S_HTTP_USERINFO_START:
		return 1;
	default:
		break;
	}

	return 0;
}

int32 HttpParser::parseUrl
	(
	const uint8* buf, 
	int32 buflen, 
	bool isConnect,
	HttpParserUrl& u
	)
{
	ParserState s;
	const uint8* p;
	HttpParserUrlField uf, old_uf;
	bool foundAt = false;

	u.port = u.fieldSet = 0;
	s = isConnect ? S_REQ_SERVER_START : S_REQ_SPACES_BEFORE_URL;
	old_uf = HPUF_MAX;

	for (p = buf; p < buf + buflen; p ++) 
	{
		s = parseUrlChar(s, *p);

		/* Figure out the next field that we're operating on */
		switch (s)
		{
		case S_DEAD:
			return 1;

		/* Skip delimeters */
		case S_REQ_SCHEMA_SLASH:
		case S_REQ_SCHEMA_SLASH_SLASH:
		case S_REQ_SERVER_START:
		case S_REQ_QUERY_STRING_START:
		case S_REQ_FRAGMENT_START:
			continue;

		case S_REQ_SCHEMA:
			uf = HPUF_SCHEMA;
			break;

		case S_REQ_SERVER_WITH_AT:
			foundAt = true;

			/* FALLTROUGH */
		case S_REQ_SERVER:
			uf = HPUF_HOST;
			break;

		case S_REQ_PATH:
			uf = HPUF_PATH;
			break;

		case S_REQ_QUERY_STRING:
			uf = HPUF_QUERY;
			break;

		case S_REQ_FRAGMENT:
			uf = HPUF_FRAGMENT;
			break;

		default:
			// assert(!"Unexpected state");
			return 1;
		}

		/* Nothing's changed; soldier on */
		if (uf == old_uf) 
		{
			u.fieldData[uf].len ++;
			continue;
		}

		u.fieldData[uf].off = p - buf;
		u.fieldData[uf].len = 1;

		u.fieldSet |= (1 << uf);
		old_uf = uf;
	}

	/* host must be present if there is a schema */
	/* parsing http:///toto will fail */
	if ((u.fieldSet & ((1 << HPUF_SCHEMA) | (1 << HPUF_HOST))) != 0) 
	{
		if (parseHost(buf, u, foundAt) != 0) 
		{
			return 1;
		}
	}

	/* CONNECT requests can only contain "hostname:port" */
	if (isConnect && u.fieldSet != ((1 << HPUF_HOST)|(1 << HPUF_PORT)))
	{
		return 1;
	}

	if (u.fieldSet & (1 << HPUF_PORT))
	{
		/* Don't bother with endp; we've already validated the string */
		uint64 v = strtoul((const int8*)(buf + u.fieldData[HPUF_PORT].off), NULL, 10);

		/* Ports have a max value of 2^16 */
		if (v > 0xffff)
		{
			return 1;
		}

		u.port = static_cast<uint16>(v);
	}

	return 0;
}

const HttpParserData& HttpParser::getHttpParserData() const
{
	return m_data;
}

bool HttpParser::isBodyFinal()
{
	return m_data.m_state == S_MESSAGE_DONE;
}

}
